/**
 * 	write on 2005-10-27 1:24:09.
 */
package org.nanhill.xmlro.gsp;

import static org.nanhill.xmlro.gsp.GspConstants.ARRAY;
import static org.nanhill.xmlro.gsp.GspConstants.BASE64;
import static org.nanhill.xmlro.gsp.GspConstants.BOOLEAN;
import static org.nanhill.xmlro.gsp.GspConstants.BOOLEAN_FALSE;
import static org.nanhill.xmlro.gsp.GspConstants.BOOLEAN_TRUE;
import static org.nanhill.xmlro.gsp.GspConstants.CODE;
import static org.nanhill.xmlro.gsp.GspConstants.COMMAND;
import static org.nanhill.xmlro.gsp.GspConstants.DOUBLE;
import static org.nanhill.xmlro.gsp.GspConstants.GSP;
import static org.nanhill.xmlro.gsp.GspConstants.ID;
import static org.nanhill.xmlro.gsp.GspConstants.INT;
import static org.nanhill.xmlro.gsp.GspConstants.NOTIFY;
import static org.nanhill.xmlro.gsp.GspConstants.NULL;
import static org.nanhill.xmlro.gsp.GspConstants.REQUEST;
import static org.nanhill.xmlro.gsp.GspConstants.RESPONSE;
import static org.nanhill.xmlro.gsp.GspConstants.SET;
import static org.nanhill.xmlro.gsp.GspConstants.SET_RESPONSE;
import static org.nanhill.xmlro.gsp.GspConstants.STRING;
import static org.nanhill.xmlro.gsp.GspConstants.STRUCT;
import static org.nanhill.xmlro.gsp.GspConstants.STRUCT_MEMBER;
import static org.nanhill.xmlro.gsp.GspConstants.STRUCT_MEMBER_NAME;
import static org.nanhill.xmlro.gsp.GspConstants.UINT;

import java.io.Reader;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.nanhill.xmlro.AbstractXmlroInput;
import org.nanhill.xmlro.Deserializer;
import org.nanhill.xmlro.DeserializerFactory;
import org.nanhill.xmlro.XmlroException;
import org.nanhill.xmlro.gsp.model.GspDeserializerFactory;
import org.nanhill.xmlro.gsp.packet.GspPacket;
import org.nanhill.xmlro.gsp.packet.NotifyPacket;
import org.nanhill.xmlro.gsp.packet.RequestPacket;
import org.nanhill.xmlro.gsp.packet.ResponsePacket;
import org.nanhill.xmlro.gsp.packet.SetServicePacket;
import org.nanhill.xmlro.gsp.packet.SetServiceResponsePacket;

/**
 * xml -> object
 * 
 * @author alin
 * 
 */
public class GspInput<E> extends AbstractXmlroInput<E> {

	private static final DeserializerFactory df = new GspDeserializerFactory();

	private GspPacket<E> gsp;

	private boolean constructed;

	{
		this.unCheckTagName();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.xmlro.input.Input#parseXml(java.io.Reader)
	 */
	public GspInput(Reader reader) throws XmlroException {
		super(reader);
	}

	public GspInput() {
		super();
	}

	@SuppressWarnings("unchecked")
	private void construct() throws GspException {
		// skip start document node
		try {
			this.next();
			this.assertStartTag(GSP);
			this.next();
			this.assertStartTag();
			String packetName = nodeName();
			if (REQUEST.equals(packetName)) {
				gsp = new RequestPacket();
			} else if (RESPONSE.equals(packetName)) {
				ResponsePacket respPacket = new ResponsePacket();
				respPacket.setCode(Integer.parseInt(this
						.nodeAttributeValue(CODE)));
				respPacket.setCommond(this.nodeAttributeValue(COMMAND));
				respPacket.setId(this.nodeAttributeValue(ID));
				this.gsp = respPacket;
			} else if (NOTIFY.equals(packetName)) {
				gsp = new NotifyPacket();
			} else if (SET.equals(packetName)) {
				gsp = new SetServicePacket();
			} else if (SET_RESPONSE.equals(packetName)) {
				SetServiceResponsePacket respPacket = new SetServiceResponsePacket();
				respPacket.setCode(Integer.parseInt(this
						.nodeAttributeValue(CODE)));
				respPacket.setCommond(this.nodeAttributeValue(COMMAND));
				respPacket.setId(this.nodeAttributeValue(ID));
				this.gsp = respPacket;
			} else {
				throw new GspException("The gsp packet type [" + packetName
						+ "] not exist.");
			}
			// skip to the inside object start tag
			this.next();
			if (this.isEndTag(packetName)) {
				this.result = null;
			} else {
				this.result = (E) this.parse();
			}
			// assert packet end tag
			this.assertEndTag(packetName);
			// skip to the gsp end tag
			this.next();
			this.assertEndTag(GSP);
		} catch (XmlroException e) {
			throw new GspException(e);
		}
		this.gsp.setBody(this.result);
	}

	public E echo() throws XmlroException {
		if (!constructed) {
			this.construct();
		}
		return this.result;
	}

	public GspPacket<E> echoGsp() throws GspException {
		if (!constructed) {
			this.construct();
		}
		return this.gsp;
	}

	private boolean isMapMemberEndTag() throws XmlroException {
		return this.isEndTag(STRUCT_MEMBER);
	}

	public void assertMapMemberEndTag() throws XmlroException {
		this.assertEndTag(STRUCT_MEMBER);
	}

	private void assertMapMemberStartTag() throws XmlroException {
		this.assertStartTag(STRUCT_MEMBER);
	}

	// public Object parse() throws XmlroException {
	// this.assertStartTag();
	// String tagName = xpp.getName();
	// if (NULL.equals(tagName))
	// return this.readNull();
	// Deserializer deserializer = df.getDeserializer(tagName);
	// if (null == deserializer)
	// throw new GspException("TagName:[" + tagName
	// + "] has no deserializer!");
	// return deserializer.readElement(this);
	// }

	public List readList() throws XmlroException {
		this.next();
		List<Object> list = new ArrayList<Object>();
		while (false == this.canEndParseArray()) {
			Object value = this.parse();
			list.add(value);
		}
		this.assertEndTag(ARRAY);
		this.next();
		return list;
	}

	public void readNull() throws XmlroException {
		this.next();
		this.assertEndTag(NULL);
		this.next();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.xmlro.input.Input#readString(java.lang.String)
	 */
	public String readString() throws XmlroException {
		String value;
		this.next();
		if (this.isEndTag(STRING)) {
			value = "";
		} else {
			value = nodeText();
			value = new String(this.decodeBase64(value));
			this.next();
			this.assertEndTag(STRING);
		}
		this.next();
		return value;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.xmlro.Input#readBase64()
	 */
	public byte[] readBase64() throws XmlroException {
		this.next();
		byte[] bys = this.decodeBase64(nodeText());
		this.next();
		this.assertEndTag(BASE64);
		this.next();
		return bys;
	}

	private String readMapMemberKey() throws XmlroException {
		return this.nodeAttributeValue(STRUCT_MEMBER_NAME);
	}

	public void assertListEndTag() throws XmlroException {
		this.assertEndTag(ARRAY);
	}

	public void assertMapEndTag() throws XmlroException {
		this.assertEndTag(STRUCT);

	}

	public Integer readInteger() throws XmlroException {
		this.next();
		int value = Integer.valueOf(nodeText());
		this.next();
		this.assertEndTag(INT);
		this.next();
		return value;
	}

	public Long readLong() throws XmlroException {
		this.next();
		long value = Long.valueOf(nodeText());
		this.next();
		this.assertEndTag(UINT);
		this.next();
		return value;
	}

	public Boolean readBoolean() throws XmlroException {
		this.next();
		boolean value;
		String text = nodeText();
		if (BOOLEAN_TRUE.equals(text))
			value = true;
		else if (BOOLEAN_FALSE.equals(text))
			value = false;
		else
			throw new GspException("boolean value [" + text + "] incorrect!");
		this.next();
		this.assertEndTag(BOOLEAN);
		this.next();
		return value;
	}

	public Double readDouble() throws XmlroException {
		this.next();
		double value = Double.valueOf(nodeText());
		this.next();
		this.assertEndTag(DOUBLE);
		this.next();
		return value;
	}

	public boolean canEndParseArray() throws XmlroException {
		if (isEndTag(ARRAY))
			return true;
		if (isEndDocument())
			return true;
		return false;
	}

	public boolean canEndParseMap() throws XmlroException {
		if (isEndTag(STRUCT))
			return true;
		if (isEndDocument())
			return true;
		return false;
	}

	public Date readDate() throws XmlroException {
		// TODO Auto-generated method stub
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.xmlro.XmlroInput#readMap()
	 */
	public Object readMap() throws XmlroException {
		next();
		Map<String, Object> map = new HashMap<String, Object>();
		// do loop if the pointed tag isn't map end tag
		while (false == canEndParseMap()) {
			// assert the pointed tag is a map member start tag
			assertMapMemberStartTag();
			String key = readMapMemberKey();
			next();
			if (isMapMemberEndTag()) {
				// if null in map member tag, skip map member end tag and
				// continue
				next();
				continue;
			} else {
				Object value = parse();
				map.put(key, value);
				// assert the map member end tag
				assertMapMemberEndTag();
				next();
			}
		}
		// assert and skip the map end tag
		assertMapEndTag();
		next();
		return map;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.xmlro.AbstractXmlroInput#getDeserializer(java.lang.String)
	 */
	@Override
	protected Deserializer getDeserializer(String nodeName) {
		return df.getDeserializer(nodeName);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.xmlro.XmlroInput#setWrap(E)
	 */
	public void setWrap(E wrapType) {
		// TODO Auto-generated method stub

	}

	public void wrapClz(Class<E> wrapClz) {
		// TODO Auto-generated method stub

	}

}
